/*
 * We are starting very fresh here. Gone are the days of using gimp for graphics. It's too distracting.
 * So here's the deal: We will complete a few games using only software solutions for drawing. All shapes,
 * colors, and anything else will be entirely written in code. 
 * 
 * As a result, these games will consist of very simple shapes and colors. The idea is to actually COMPLETE
 * them and then release them for general consumption (or at the very least be happy enough with them to
 * do so if I wanted). 
 * 
 * This first game is a simple shooter. You are a blue block and your task it to shoot yellow blocks as they
 * fall from the sky. The player can do three things: Move left/right and shoot. The yellow blocks fall at 
 * random starting locations at a constant rate. Nothing fancy here.
 * 
 * Also, we are just using a timer rather than a true thread.
 * 
 * TODO: Create a JPanel with a black background. (400,300)
 * TODO: Create a pblock class for player data.
 * TODO: Create a yblock class for enemy data.
 * TODO: Add game logic.
 * TODO: Finish
 */
package block;

import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import javax.swing.JPanel;
import javax.swing.Timer;

// Our class. The "extends JPanel" means that this class inherits
// all of the properties of a JPanel. This means that it behaves
// and has access to all of the JPanel behaviors and methods.
// The "implements Runnable" allows this class to become threaded.
public class Main extends JPanel implements Runnable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L; // Don't worry about this.
  // It doesn't have to be here, but for a "deployed" (finished) project it is
  // important for Java. It will run without it just fine.
	
  // Define an instance of the Thread class called (surprise!) "thread".
  private Thread thread;
  
  // Define a player instance (see the pblock class)
	private pblock player;
  // Define an array of yblocks called yellows. 
	private ArrayList<yblock> yellows;
   
  // Notice these are all defines, we haven't created an instance yet...
   
  // Are we in game?
	private boolean ingame;
  // Rudimentary score.
	private int blocksDestroyed;
	
	public Main() {
		
    // Make our class able to listen to the keyboard. The "TAdapter" class
    // is an anonymous inner class, which means we make a mini class within 
    // a bigger class, mostly for convenience. It extends KeyAdapter.
		addKeyListener(new TAdapter());
  
    // Allow the JPanel to have the focus of the keyboard and mouse. Otherwise
    // your inputs won't be read. 
		setFocusable(true);
   
    // Set the background to a boring color.
		setBackground(Color.BLACK);
    
    // Turns Double Buffering on. Smooths things out nicely in terms of jerkiness.
    // Read wikipedia for more info. The graphics system does this automatically 
    // so no further code is required. 
    setDoubleBuffered(true);
    
    // We are in game!  
		ingame = true;
		
    // Set the size of the JPanel. If this is bigger than the JFrame we are adding
    // it to you won't see the entire thing. Likewise if it is smaller there will
    // be a border on the bottom and right.
		setSize(400, 300);
		
    // Create our player instance.  
		player = new pblock();
		
     // Call the initYellows method.
		initYellows();
		
		// This timer is to generate the falling blocks at a desired interval.
		Timer timer = new Timer (1500, new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				generateYblock();
			}
		});
		timer.start();
	}
	
  // Begins our thread.
	public void addNotify() {
		super.addNotify();
		thread = new Thread(this);
		thread.start();

	}
	
  // Creates an array of "yblock" objects and sets our
  // initial score to 0. 
	public void initYellows() {
		yellows = new ArrayList();
		blocksDestroyed = 0;
		
	}
	
	public void paint(Graphics g) {
		super.paint(g);
		
		if (ingame) {
			
      // Create graphics system. You could theoretically do all this
      // using just g as well.
			Graphics2D g2d = (Graphics2D)g;
			
      // If the player isn't dead, paint him to the screen!.
			if (player.isVisible()) { 
				g2d.setColor(Color.blue);
				g2d.fillRect(player.getX(), player.getY(), player.getWidth(), player.getHeight());
			}
      
      // Define our array of shots. The <> following ArrayList refers to what are called "Generics".
      // Without going into too much detail here, they basically allow (when applied) Java to assume
      // that the contents of the array are all of the type inside the <>. Otherwise you have to cast
      // to the type you want every time you access an object from the array since you don't know what
      // it ~could~ be. 
			ArrayList<?> pShots = player.getPshots();
			
      // For every element (shot) inside the pShots array we draw it!   
			for (int i = 0; i < pShots.size(); i++) {
				g2d.setColor(Color.GREEN);
        
        // Here's where you see the casting required. If I don't include the (pShot) then the compiler
        // will complain. So to remove this I could (in the code above) change it to ArrayList<pShot>
        // and avoid having to cast each time. Why didn't I? Because I didn't know how to use generics
        // when I first wrote this code. 
        
        // Anyway, this code gets a pShot from the array, then draws it to the screen using its specific
        // attributes.
				pShot s = (pShot)pShots.get(i);
				g2d.fillRect(s.getX(), s.getY(), s.getWidth(), s.getHeight());
			
			}
			
      // This loop does the same thing, except instead of shots it paints the array of visible falling blocks.
			for (int i = 0; i < yellows.size(); i ++) {
				yblock y = (yblock)yellows.get(i);
				if (y.isVisible()) {
					g2d.setColor(Color.YELLOW);
					g2d.fillRect(y.getX(), y.getY(), y.getWidth(), y.getHeight());
				}
			}
			
      // Show the player the score.   
			g2d.setColor(Color.white);
			g2d.drawString("Yellow blocks destroyed: " + blocksDestroyed, 5, 15);
		} 
      else { // The player isn't alive, show end message.
			String msg = "Game Over";
			Font small = new Font("Helvetica", Font.BOLD, 14);
			g.setColor(Color.white);
			g.setFont(small);
			g.drawString(msg, 200, 150);
		}
		
    // Prevents shearing/tearing on Linux systems.
		Toolkit.getDefaultToolkit().sync();
    
    // Dispose of our graphics system.  
		g.dispose();
	}
	
  // Step things along in this cycle. This moves all the non player things.
  // First it moves each shot, then each block. 
	public void actionCycle() {
		
		ArrayList pShots = player.getPshots();
		
		for (int i = 0; i < pShots.size(); i ++) {
			pShot s = (pShot) pShots.get(i);
			if (s.isVisible())
				s.move();
			else pShots.remove(i); // Shot left screen, delete it to free space.
		}
		
		for (int i = 0; i < yellows.size(); i ++) {
			yblock y = (yblock) yellows.get(i);
			if (y.isVisible())
				y.move();
			else yellows.remove(i); // Yellow destroyed or fell, delete to free space.
		}
		

	}
	
  // Read the name :-)
	public void generateYblock() {
		// creates a random number ( 1 -5 ) blocks to drop.
		int num = (int)(Math.random()*5);
		for (int i = 0; i < num; i ++)	
		// Adds the blocks to the array.
		yellows.add(new yblock());
	}
	
   
	public void checkCollisions() {
		
		Rectangle pRect = player.getBounds(); // Creates a rectangle the size and position of the player.
		
		for (int j = 0; j < yellows.size(); j ++) {
			yblock y = (yblock) yellows.get(j);
			Rectangle yRect = y.getBounds();  // Creates a rectangle the size and position of the block.
			
			if (pRect.intersects(yRect)) {  // If they intersect (.intersects() is a method in the awt.Rectangle class)
				player.setVisible(false);  // Make player invisible
				y.setVisible(false); // Make block invisible
				ingame = false; // End the game
			}
		}
		
    // Get list of shots and do the same thing as above, except instead of 
    // the player intersecting a block, it's a shot intersecting a block.
		ArrayList pShots = player.getPshots();
		
		for (int i = 0; i < pShots.size(); i ++) {
			pShot s = (pShot) pShots.get(i);
			
			Rectangle sRect = s.getBounds();
			
			for (int j = 0; j < yellows.size(); j ++) {
				yblock y = (yblock) yellows.get(j);
				Rectangle yRect = y.getBounds();
				
				if (sRect.intersects(yRect)) {
					y.setVisible(false); // Block no longer visible
					s.setVisible(false); // Shot no longer visible
					blocksDestroyed +=1; // Increment score!
				}
			}
		}
		
	}
	
   
  // Anonymous inner class for KeyEvents. This captures keyboard input data, then
  // passes it on to the two methods inside. 
	private class TAdapter extends KeyAdapter {
    // If a key is released, pass the keyEvent (e) to the player.KeyReleased() method
    // for processing
		public void keyReleased(KeyEvent e) {
			player.KeyReleased(e);
		}
    // Same for key press. Order of these methods doesn't matter (in case you're wondering).
    // It never does. 
		public void keyPressed(KeyEvent e) {
			player.KeyPressed(e);
		}
	}
	
  // This is what gets its own thread. Basically it's just another line of execution.
  // This is required because we are using Swing. If we pile a lot of code into the 
  // main swing thread (the default) then the GUI can suffer immensly and so does the
  // excecution of our code. So in order to keep things running smoothly we segregate
  // the execution.
  // More info -> http://www.javamex.com/tutorials/threads/swing_ui.shtml
  
	public void run() {
		
		long beforeTime, timeDiff, sleep; // For thread
		beforeTime = System.currentTimeMillis(); //
		int DELAY = 5; //
		
		while(true) {
			
      // This is our game loop.   
			actionCycle(); // Do the action cycle
			player.move(); // Move the player
			checkCollisions(); // Check collisions
			repaint(); // Repaint to show the change!
      // And that's pretty much it!
			
      // This code is for proper threading handling. It tells our thread
      // to sleep when not doing things so that other threads on the machine
      // can process their data. 
			timeDiff = System.currentTimeMillis() - beforeTime;
			sleep = DELAY - timeDiff;
			
			if (sleep < 0)
				sleep = 2;
			try {
				Thread.sleep(sleep);
			} catch (InterruptedException e) {
				System.out.println("Interrupted");
			}
		
			beforeTime = System.currentTimeMillis();
		}

	}

	

}
